
#pragma once

#include "ublocks.h"

namespace EggNine {

class LightsBlock : public UniformBlock {
public:
    struct SLight {
        vec4 m_pos;
        vec4 m_color;
    };

    LightsBlock(const char* name)
        : UniformBlock(name, sizeof(SLight)*MAX_LIGHTS, NULL, GL_DYNAMIC_DRAW)
    {
        m_lights = (SLight*)_aligned_malloc(m_size, 16);
        m_data=m_lights;
    }
    ~LightsBlock()
    {
        _aligned_free(m_lights);
    }

    const vec4& LightPos(int light) const { return m_lights[light].m_pos; }
    const vec4& LightColor(int light) const { return m_lights[light].m_color; }
    vec4& LightPos(int light) { m_dirty=true; return m_lights[light].m_pos; }
    vec4& LightColor(int light) { m_dirty=true; return m_lights[light].m_color; }

private:
    SLight *m_lights;
};

class CameraBlock : public UniformBlock {
public:
    struct SMatrices {
        mat4 Projection;
        mat4 Camera;
    } m_Matrices;

    CameraBlock(const char* name)
        : UniformBlock(name, sizeof(SMatrices), NULL, GL_DYNAMIC_DRAW)
    {
        m_data=&m_Matrices;
    }

    const mat4& Camera() const { return m_Matrices.Camera; }
    const mat4& Projection() const { return m_Matrices.Projection; }
    mat4& Camera() { m_dirty=true; return m_Matrices.Camera; }
    mat4& Projection() { m_dirty=true; return m_Matrices.Projection; }
};

class ModelBlock : public UniformBlock {
public:
    struct SModel {
        mat4 ModelView;
        __m128 NormalM[3];
    };
    ModelBlock(const char* name)
        : UniformBlock(name, sizeof(SModel), NULL, GL_STREAM_DRAW)
        , m_Model(NULL)
    {
    }
    ~ModelBlock()
    {
    }

    void SetData(void* data) { throw "Can't copy data to this block"; }
    void SetData(void* data) const { m_data=data; m_dirty=true; }

private:
    const SModel* m_Model;
};

class MaterialBlock : public UniformBlock {
public:
    struct SMaterial {
        vec4 diffuse;
        vec4 extra;
    };

    MaterialBlock(const char* name)
        : UniformBlock(name, sizeof(SMaterial), NULL, GL_STREAM_DRAW)
        , m_material(NULL)
    {}
    ~MaterialBlock()
    {}

    void SetData(void* data) { throw "Can't copy data to this block"; }
    void SetData(void* data) const { m_data=data; m_dirty=true; }

private:
    const SMaterial* m_material;
};

class WaveSimBlock : public UniformBlock {
public:
    struct WaveSimParams {
        float invDhSq;
        float cSq;
        float dt;
        float impulsePre;
    };

    WaveSimBlock(const char* name)
        : UniformBlock(name, sizeof(WaveSimParams), NULL, GL_STREAM_DRAW)
        , m_waveSimParams(NULL)
    {}
    ~WaveSimBlock()
    {}

    void SetData(void* data) { throw "Can't copy data to this block"; }
    void SetData(void* data) const { m_data=data; m_dirty=true; }

private:
    const WaveSimParams* m_waveSimParams;
};

}